package motifs;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.LinkedList;

import package_traitement.MatriceCooccurrence;
import package_traitement.MatriceTonGris;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

/**
 * Classe representant chaque empreinte composant le motif
 * @author Alexandre Masson
 *
 */
public class Empreinte {
	private String filename;
	private String parentname;
	private BufferedImage parent;
	private MatriceTonGris mtGris;
	private MatriceCooccurrence [][] lesCooccurences ;
	private int [] lesAngles ;
	private int AnglesSize;
	private int pas_max;
	private LinkedList<Integer> CodeFreeman;
	private int [] histogrammeSimple;
	private int [][] histogrammeDouble;
	private int [][][] histogrammeTriple;

	/**
	 * Constructeur
	 * 
	 * @param i
	 * 			- BufferedImage de l'empreinte, du motif connexe
	 * 
	 * @param filString
	 * 			- nom du fichier de sortie pour la sauvegarde
	 * 
	 * @param ps
	 * 			- nom du fichier d'image globale
	 * 
	 * @param la
	 * 			- tableau d'entier contenant les angles a traiter pour les matrice de cooccurences
	 * 
	 * @param as
	 * 			- taille du tableau la
	 * 
	 * @param pa
	 * 			- pas max a analyser
	 */
	public Empreinte( BufferedImage i,String filString, String ps, int[] la, int as, int pa){
		parentname = ps;
		filename = filString;
		lesAngles =la;
		AnglesSize = as;
		this.setParent(i);
		mtGris = new MatriceTonGris(this.parent);
		pas_max =pa;
		histogrammeSimple = new int[8];
		histogrammeDouble = new int [8][8];
		histogrammeTriple = new int [8][8][8];
		genCooccurrences();
		genCodeFreeman();
		genHistoSimple();
		genHistoDouble();
		genHistoTriple();
		//System.out.println("Empreinte cree!!");
		//saveJPEG();
	}

	/**
	 * Methode qui genere l'histogramme pour des combinaisons de trois directions
	 */
	private void genHistoTriple() {
		// TODO Auto-generated method stub
		for(int z= 0; z < 8; z++){
			for(int y = 0; y < 8; y++){
				for(int x = 0; x < 8; x++){
					histogrammeTriple[x][y][z]=0;
				}
			}
		}
		if(CodeFreeman != null){
			for(int i = 0; i < CodeFreeman.size()-2; i++){
				histogrammeTriple[CodeFreeman.get(i)][CodeFreeman.get(i+1)][CodeFreeman.get(i+2)]++;
			}
		}
	}

	/**
	 * Methode qui genere l'histogramme pour des combinaisons de deux directions
	 */
	private void genHistoDouble() {
		for(int ii = 0 ; ii <8 ;ii++){
			for(int jj= 0 ; jj<8 ; jj++){
				histogrammeDouble[ii][jj]=0;
			}
		}
		if(CodeFreeman != null){
			for(int i = 0 ; i < CodeFreeman.size()-1 ; i++){
				histogrammeDouble[CodeFreeman.get(i)][CodeFreeman.get(i+1)]++;
			}
		}

	}

	/**
	 * Methode qui genere un histogramme simple.
	 */
	private void genHistoSimple() {
		for(int j = 0 ; j< 8; j++){
			histogrammeSimple[j] = 0;

		}
		if(CodeFreeman != null){
			for(int i= 0 ; i< CodeFreeman.size() ; i++){
				histogrammeSimple[CodeFreeman.get(i)]++;
			}
		}

	}

	/**
	 * Methode qui genere les codes de Freeman du motif.
	 */
	private void genCodeFreeman(){
		CodeFreeman = new LinkedList<Integer>();
		boolean trouv = false;
		int xdebut = 0;
		int ydebut = 0;
		for(int i=0; i< parent.getWidth() && !trouv;i++){
			for(int j = 0 ; j< parent.getHeight() && !trouv; j++){
				if(parent.getRGB(i, j) != Color.WHITE.getRGB()){
					trouv = true;
					xdebut = i;
					ydebut =  j;
				}
			}
		}
		//System.out.println("debut trouve");
		int xcible = xdebut ; 
		int ycible = ydebut ;
		int directioncible = 0;
		int aux = 0;
		boolean first = true, finish = false;
		while(! finish){

			aux = findDir(xcible, ycible, directioncible);
			if(first && aux == directioncible){
				//cas ou le pixels le plus haut gauche renverrai vers encore plus haut, impossible
				break;
			}
			if (first && aux != directioncible){
				first = false;
			}
			if(!first){
				directioncible = aux;
			}
			//System.out.println("dir trouve");
			CodeFreeman.add(directioncible);
			switch (directioncible) {
			case 0:  ycible-- ;break;
			case 1: xcible++ ; ycible-- ;break;
			case 2:  xcible++ ;break;
			case 3: xcible++; ycible++; break;
			case 4: ycible++;break;
			case 5: xcible--; ycible++;break;
			case 6: xcible--;break;
			case 7: xcible--; ycible--;break;

			default:
				break;
			}
			directioncible = (directioncible +5) %8;
			if(xcible == xdebut && ycible== ydebut){
				finish = true;
			}
		}
		System.out.println("code trouve");
	}


	/**
	 * Fonction qui pour un pixel et une direction de depart donne,le pixels suivant a dessiner pour continuer  le motif, 
	 * fonction utilisee dans la generation des codes de Freeman.
	 * 
	 * @param xdebut
	 * 				- abscisse du pixel source.
	 * 
	 * @param ydebut
	 * 				- ordonnee du pixel source.
	 * 
	 * @param directioncible
	 * 				- Direction du dernier trait trace, utile pour savoir ou commncer a chercher le pixel suivant.
	 * @return
	 * 				- retourne un int entre 0 et 7 qui donne la direction du pixel suivant dasn le motif, 0 etant le nord, 2 l'est, 4 le sud, 6 l'ouest, etc...
	 */
	private int findDir(int xdebut, int ydebut, int directioncible) {
		int aux  = ( directioncible +1 ) % 8; // la direction suivate a tester
		boolean trouve = false;
		while (!trouve && aux != directioncible){
			//System.out.println("empreinte.findir.aux : "+aux);
			switch (aux){
			case 0: if(ydebut>= 1 && !trouve){
				if(parent.getRGB(xdebut, ydebut-1) != Color.WHITE.getRGB()){
					trouve= true;
				}
				else{
					aux = (aux+1) %8;
				}
			}else{
				aux = (aux+1) %8;
			}
			break;
			case 1: if(ydebut >=1 && xdebut < parent.getWidth()-1 &&!trouve){
				if(parent.getRGB(xdebut+1, ydebut-1)!=Color.WHITE.getRGB()){
					trouve = true;
				}
				else{
					aux = (aux+1) %8;
				}
			}else{
				aux = (aux+1) %8;
			}
			break;
			case 2: if(xdebut< parent.getWidth()-1 &&!trouve){
				System.out.println(xdebut+" "+parent.getWidth()+" "+ydebut+" "+parent.getHeight());
				if(parent.getRGB(xdebut+1, ydebut)!= Color.WHITE.getRGB()){
					trouve = true;
				}
				else{
					aux = (aux+1) %8;
				}
			}else{
				aux = (aux+1) %8;
			}
			break;
			case 3:  if(xdebut < parent.getWidth()-1 && ydebut < parent.getHeight()-1 &&!trouve){
				if (parent.getRGB(xdebut+1, ydebut+1)!= Color.WHITE.getRGB()){
					trouve = true;
				}
				else{
					aux = (aux+1) %8;
				}
			}else{
				aux = (aux+1) %8;
			}
			break;
			case 4: if(ydebut < parent.getHeight()-1 && !trouve){
				if(parent.getRGB(xdebut, ydebut+1) != Color.WHITE.getRGB()){
					trouve = true;
				}
				else{
					aux = (aux+1) %8;
				}
			}else{
				aux = (aux+1) %8;
			}
			break;
			case 5: if( xdebut >= 1 && ydebut < parent.getHeight()-1 &&!trouve){
				if(parent.getRGB(xdebut-1, ydebut+1) != Color.WHITE.getRGB()){
					trouve = true;
				}
				else{
					aux = (aux+1) %8;
				}
			}else{
				aux = (aux+1) %8;
			}
			break;
			case 6: if(xdebut >= 1 && !trouve){
				if(parent.getRGB(xdebut-1, ydebut)!= Color.white.getRGB()){
					trouve = true;
				}
				else{
					aux = (aux+1) %8;
				}
			}else{
				aux = (aux+1) %8;
			}
			break;
			case 7: if(xdebut >=1 && ydebut >= 1 && !trouve){
				//System.out.println("aux = 7 et coord valide"+ xdebut +" "+ ydebut);
				if(parent.getRGB(xdebut-1, ydebut-1) != Color.WHITE.getRGB()){
					trouve = true;
				}
				else{
					aux = (aux+1) %8;
				}
			}
			else{
				aux = (aux+1) %8;
			}
			break;
			default: System.err.println("pasage en defaut dans empreinte::finddir");break;
			}

		}
		return aux;
	}

	/**
	 * Fonction qui retourne la liste contenant les codes de Freeman de l'empreinte
	 * 
	 * @return
	 * 			- une liste chainee contenant les codes de Freeman du motif
	 */
	public LinkedList<Integer> getCodeFreeman(){
		if(CodeFreeman == null){
			genCodeFreeman();
			return CodeFreeman;
		}
		else return CodeFreeman;
	}


	private void setParent(BufferedImage parent) {
		this.parent = parent;
	}

	public BufferedImage getParent() {
		return parent;
	}

	/**
	 * Methode qui genere les matrices de cooccurrences
	 */
	private void genCooccurrences(){
		lesCooccurences = new MatriceCooccurrence [AnglesSize][pas_max];
		for(int i = 0 ; i< AnglesSize ;i++){
			for(int j = 0; j< pas_max ; j++){
				lesCooccurences[i][j]= new MatriceCooccurrence(mtGris.getMatrice(),
						mtGris.getDx(),
						mtGris.getDy(), 
						lesAngles[i], 
						j+1, 
						ImageATraiter.NIVEAUX_GRIS);
				//	System.out.println(lesCooccurences[i][j].toString());
			}
		}
	}
	
	/**
	 * Fonction qui permet de recuperer les histogramme linearises dans une chaine de caracteres
	 * 
	 * @return
	 * 		- un chaine qui contient les histogrammes simple,double,et triple, mais dans une seule ligne
	 */
	public String getLinearHistograms(){
		StringBuffer sbuf = new StringBuffer();
		for(int i =0; i< 8 ; i++){
			sbuf.append(histogrammeSimple[i]);
			sbuf.append(" ");
		}
		for(int i = 0 ; i < 8 ; i++){
			for(int j = 0 ; j < 8; j++){
				sbuf.append(histogrammeDouble[j][i]);
				sbuf.append(" ");
			}
		}
		for(int z = 0 ; z < 8 ; z++){
			for(int y = 0; y < 8 ; y++){
				for(int x = 0; x < 8 ; x++){
					sbuf.append(histogrammeTriple[x][y][z]);
					sbuf.append(" ");
				}
			}
		}
		return sbuf.toString();
	}

	/**
	 * Methode qui permet de sauvegarder l'image de l'empreinte dans un fichier jpef, pour affichage plus tard
	 * 
	 * @param filename
	 * 				- nom complet du fichier a sauvegarder
	 */
	public void saveJPEG(String filename) {

		try {
			OutputStream out = new FileOutputStream(filename);
			JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
			encoder.encode(parent);
			out.close();
			//System.out.println("la courbe a ete sauvee dans le fichier "+filename);
		} catch (Exception e) {
			System.out.println("Impossible de sauver la courbe : "+e);
		}
	} 

	/**
	 * Methode qui permet de parser l'empreinte en String, utile pour l'affichage et la sauvegarde
	 */
	public String toString(){
		StringBuffer sbuf = new StringBuffer();
		for(int p = 0 ; p< pas_max; p++){
			for(int a = 0 ; a < AnglesSize ;a++){
				sbuf.append(lesCooccurences[a][p].toString());
				//sbuf.append(" ");
			}
		}

		sbuf.append(filename);
		sbuf.append(" ");
		sbuf.append(parentname);
		sbuf.append(" ");
		sbuf.append(CodeFreeman.size());
		sbuf.append(" ");
		for(int i = 0 ; i< CodeFreeman.size() ; i++){
			sbuf.append(CodeFreeman.get(i));
			sbuf.append(" ");
		}
		return sbuf.toString();
	}

}
